package model;

import java.util.ArrayList;
import java.util.TreeMap;

import basics.ConversaoDeBases;
import exceptions.ExecaoHalt;
import exceptions.ExecaoInstrucaoInexistente;
import exceptions.ExecaoMontador;

public class Simulador {

	private TreeMap<String, Registrador> mapaReg;
	protected Memoria memoria;

	private UnidadeDeControle UC;
	private ULA ula;

	public Simulador() {
		inicializaSimulador();
	}

	public void inicializaSimulador() {
		this.mapaReg = new TreeMap<String, Registrador>();
		this.memoria = new Memoria();
		this.ula = new ULA();
		defineRegistradores();

		UC = new UnidadeDeControle(mapaReg, memoria, ula);
	}

	public ArrayList<LinhaDeExecucao> executaMontagem(ArrayList<String> assemblyCode) throws ExecaoMontador {
		// limpa a memória
		memoria.inicializaMemoria();
		return UC.assemblyToBin(assemblyCode);
	}

	public void executaProximaInstrucao() throws ExecaoHalt, ExecaoInstrucaoInexistente {

		String pcAux = UC.getPc();
		// executa pc
		try {
			UC.executaInstrucao(ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecUnsigned(UC.getPc())));

		} catch (ExecaoInstrucaoInexistente e) {
			// TODO Auto-generated catch block
			throw e;
		} catch (ExecaoHalt e) {
			// TODO Auto-generated catch block
			throw e;
		}

		// caso de jump, para evitar pulos redundantes
		if (pcAux.equals(UC.getPc())) {
			// incrementa o PC
			UC.incrementaPc();
		}

	}

	public void executaTodasInstrucoes() throws ExecaoInstrucaoInexistente, ExecaoHalt {
		UC.executaTodasInstrucoes();
	}

	public String[] getMemoria(long start, long end) {

		int j = 0;

		// aqui deve lançar uma exceção caso (end - start) < 0 ou se (end+1) ou
		// start não forem múltiplos de 4
		if ((((end - start) < 0) || ((end + 1) % 4 != 0) || (start % 4 != 0))
				|| ((end - start) > memoria.TAMANHO))
			return null;

		int length = (int) (((end - start) + 1));

		String[] memoriaArray = new String[length];

		for (long i = start; i < end + 1; i++) {
			memoriaArray[j] = memoria.get(ConversaoDeBases.DecToBin(i));
			j++;
		}

		return memoriaArray;
	}

	public String[] getRegistradores() {
		String[] valorRegistradores = new String[32];

		for (int i = 0; i < mapaReg.size(); i++) {
			valorRegistradores[i] =
					mapaReg.get(ConversaoDeBases.DecToBin(i, 5)).getValor();
		}

		return valorRegistradores;
	}

	public long tamanhoMemoria() {
		return memoria.TAMANHO;
	}

	public long pc() {
		return ConversaoDeBases.binToDecUnsigned(UC.getPc());
	}

	private void defineRegistradores() {
		Registrador r;

		r =
				new Registrador("$zero", "00000", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$at", "00001", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$v0", "00010", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$v1", "00011", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$a0", "00100", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$a1", "00101", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$a2", "00110", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$a3", "00111", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t0", "01000", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t1", "01001", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t2", "01010", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t3", "01011", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t4", "01100", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t5", "01101", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t6", "01110", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t7", "01111", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s0", "10000", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s1", "10001", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s2", "10010", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s3", "10011", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s4", "10100", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s5", "10101", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s6", "10110", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$s7", "10111", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t8", "11000", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$t9", "11001", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$k0", "11010", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$k1", "11011", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$gp", "11100", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$sp", "11101", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$fp", "11110", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
		r = new Registrador("$ra", "11111", "00000000000000000000000000000000");
		mapaReg.put(r.getCodigo(), r);
	}
}
